﻿const code = require('../constant/code');
const roomProto = require('./roomFrameProto');
const dao = require('../dao/commonDao');
const pushAPI = require('../API/pushAPI');
const logger = require('pomelo-logger').getLogger('logic');
const utils = require('../util/utils');
const scheduler = require('pomelo-scheduler');
const enumeration = require('../constant/enumeration');
const userDao = require('../dao/userDao');
const userInfoServices = require('../services/userInfoServices');
const pomeloServices = require('../services/pomeloServices');
const redisService = require('../services/redisService');
const gameDataService = require('../services/gameDataServices');
const pomelo = require('pomelo');
const moment = require('moment');
const logServices = require('../services/logServices');
const gameConfig = require('../gameComponent/gameConfig');

moment.suppressDeprecationWarnings = true;

class roomBaseFrame {

    constructor(roomID, creatorInfo, gameRule) {

        this.app = pomelo.app;

        //房间基本信息
        this.roomID = roomID; //房间ID
        this.bankerUid = null; //庄家uid
        this.createAndWatch = gameRule.createAndWatch || false; //true表示玩家进入只能旁观
        this.gameType = gameRule.gameType; //游戏类型
        this.gameRule = gameRule; //游戏规则
        this.creatorInfo = creatorInfo; //创建信息
        this.unionRoom = this.isCreateByUnion(creatorInfo); //是否是联盟创建的房间
        this.goldRoom = this.isCreateByGold(creatorInfo); //是否是金币房间
        this.baseScore = gameRule.baseScore || 1; //游戏底分

        //房间状态
        this.createTime = moment(); //创建时间
        this.gameStarted = false; //游戏单局是否开始
        this.roomGameStarted = false; //游戏整体是否已经开始
        this.isCanNotReady = {}; //是否可以准备
        this.isCanStart = true; //游戏是否可以开始
        this.autoReadyTimer = {}; //玩家准备定时器
        this.isSaveRecords = false; //打完一局才可以保存记录
        this.payDiamondUser = {};
        //解散信息
        this.roomDismissed = false; //游戏是否已经解散
        this.askForExitArr = []; //玩家请求解散
        this.dismissRoomTimer = null; //解散房间定时器
        this.askDissmissTime = null; //请求解散时间

        //局数信息
        this.maxBureau = gameRule.bureau || 1; //游戏总局数
        this.curBureau = 0; //游戏局数

        //房间用户信息
        this.userWinTotalScore = {}; //玩家总输赢
        this.chairCount = gameRule.maxPlayerCount || 2; //玩家总人数
        this.userArr = {}; //所有玩家
        this.rabateUser = {}; //返利对象
        this.payTicketUser = []; //已经支付门票的玩家
        this.askDissmissCount = {};


        //房间记录数据
        this.userWinRecord = {}; //用户输赢记录
        this.reviewRecord = []; //牌面回顾记录
        this.gameVideoRecord = []; //视频回放记录
        this.userTotalScore = {}; //带入分数类型游戏的玩家总分数

        this.gameLogger = logServices.getLogByType(this.gameType); //日志
        this.userDismissList = {}; //玩家离座请求
        this.userRobotTotalScore = {};
        logger.info(`[${this.roomID}] create new room success gameType = ${this.gameType}`);

    }

    /*---------------------------------消息接收相关----------------------------------*/


    receiveRoomMessage(uid, msg) {

        let type = msg.type || null;
        let data = msg.data || null;
        if (!type || !data || !this.userArr[uid]) return;

        if (type === roomProto.USER_READY_NOTIFY) {
            //玩家准备
            this.userReady(uid, data);
        } else if (type === roomProto.USER_LEAVE_ROOM_NOTIFY) {
            //玩家请求离开房间
            this.userAskLeaveRoom(uid);
        } else if (type === roomProto.GET_ROOM_SCENE_INFO_NOTIFY) {
            //玩家获取场景消息
            this.getRoomSceneInfo(uid);
        } else if (type === roomProto.ASK_FOR_DISMISS_NOTIFY) {
            //玩家请求解散
            this.askForDismiss(uid, data).catch(err => logger.error(err.stack));
        } else if (type === roomProto.USER_CHANGE_SEAT_NOTIFY) {
            //玩家中途坐下
            this.userChangeSeat(uid, data.fromChairID, data.toChairID);
        } else if (type === roomProto.USER_CHAT_NOTIFY) {
            //玩家聊天
            this.userChat(uid, data.type, data.msg, data.recipientID);
        } else if (type === roomProto.USER_GIFT_SEND_NOTIFY) {
            //玩家赠送礼物
            this.userSendGift(uid, data.toChairID, data.giftID);
        } else if (type === roomProto.GAME_INVITATION_NOTIFY) {
            //玩家邀请
            this.gameInvitation(uid, data);
        } else {
            this.showLog(`receiveRoomMessage error type = ${type} not find`, true);
        }
    }

    /*-------------------------房间基础消息---------------------------------*/

    //2.0 玩家准备
    userReady(uid) {

        let user = this.userArr[uid];
        if (!user) { return; }

        if (this.curBureau >= this.maxBureau) {
            return;
        }

        this.showLog(`[${uid}] user ready begin`);

        //如果在离开状态 不能退出
        if (this.isCanNotReady[uid]) {
            this.showLog(`[${uid}] user ready fail can not ready kick auto`, true);
            return;
        }

        if (!this.gameStarted && user.playStatus == roomProto.userPlayEnum.NONE) {
            //更改玩家状态为准备
            this.changePlayStatus(uid, roomProto.userPlayEnum.READY);
            this.sendRoomData(roomProto.userReadyPush(user.chairID, uid));
            if (this.autoReadyTimer[uid]) {
                this.showLog(`[${uid}] user ready and close ready timer`);
                clearTimeout(this.autoReadyTimer[uid]);
                delete this.autoReadyTimer[uid];
            }
            //this.showLog(`[${uid}] user ready success`);

        } else {
            // this.showLog(`[${uid}] user ready fail status = ${user.playStatus} gameStarted = ${this.gameStarted}`);
            return;
        }
        //游戏是否可以开始
        this.checkIsCanStartGame(uid);
    }

    //2.0判断是否可以开始游戏
    checkIsCanStartGame(uid) {

        if (!this.isCanStart || this.roomDismissed || this.gameStarted) {
            //小局开始了或者游戏解散了 游戏不能开始
            this.showLog(`game can not start gameStarted = ${this.gameStarted} canStart = ${this.isCanStart} roomDismissed = ${this.roomDismissed}`, true);
            return;
        } else {

            let readyCount = (this.getReadyUserList()).length; //获取已经准备的玩家
            let userCount = (this.getGameUserList()).length; //获取当前游戏中的玩家
            let canStartGame = false;  //判断游戏是否可以开始
            //必须人满开局的游戏
            if (gameConfig.isFullStart(this.gameType)) {
                //是否可以开始游戏
                if ((userCount === readyCount && userCount >= this.gameRule.minPlayerCount) || readyCount === this.gameRule.maxPlayerCount) {
                    canStartGame = true;
                }
            } else {
                //多人游戏开始游戏
                let startCount = this.gameRule.startCount || 2;
                if (readyCount >= startCount) {
                    canStartGame = true;
                }
            }
            this.showLog(`[${uid}] game start result userCount = ${userCount} readyCount = ${readyCount}  minCount = ${this.gameRule.minPlayerCount} maxCount = ${this.gameRule.maxPlayerCount} canStartGame = ${canStartGame}`);
            //游戏可以开始
            if (canStartGame) {
                //更改房间状态 游戏已经开始
                this.gameStarted = true; //游戏已经开始
                this.roomGameStarted = true; //当前轮数开始
                //更新游戏状态
                this.resetPlayerStatus(roomProto.userPlayEnum.PLAYING);
                this.showLog(`[${uid}] user ready end and start game`);
                this.startGame().catch((err) => { logger.error(err.stack); });
            }
        }
    }

    //玩家请求离开房间
    userAskLeaveRoom(uid, isMaster) {

        this.showLog(`[${uid}] user ask leave room begin isMaster = ${isMaster}`);
        let user = this.userArr[uid] || null;
        if (user) {

            if (this.roomGameStarted) {
                //游戏开始后 只能没有在座位上的玩家离开
                if ((user.chairID >= this.chairCount && user.playStatus == roomProto.userPlayEnum.WATCH) || (this.userDismissList[uid] && !this.gameStarted)) {
                    this.showLog(`[${uid}] watch user leave success chairID = ${user.chairID} playStatus = ${user.playStatus} roomGameStarted = ${this.roomGameStarted} `);
                    this.userLeaveRoom(uid).catch((err) => {
                        logger.error(new Error(err.stack));
                    });
                    return code.OK;
                } else if (!this.userDismissList[uid]) {

                    //如果金币房 直接退出
                    if (this.goldRoom) {
                        this.showLog(`[${uid}] leave gold room`);
                        this.userLeaveRoom(uid).catch((err) => { logger.error(new Error(err.stack)); });
                        this.dismissRoom(enumeration.gameRoomDismissReason.USER_DISMISS);
                        return code.OK;

                    } else {
                        this.userDismissList[uid] = true;
                        this.showLog(`[${uid}]game started playing user leave success uid = ${uid} chairID = ${user.chairID} playStatus = ${user.playStatus}  `, true);
                        if (!isMaster) {
                            this.sendPopDialogContent(code.APPLICATIOM_SUCCEED, '申请离开成功', [uid]);
                        }
                        return code.CAN_NOT_LEAVE_ROOM;
                    }
                } else if (this.userDismissList[uid]) {
                    this.showLog(`[${uid}]game started playing user leave fail uid = ${uid} chairID = ${user.chairID} playStatus = ${user.playStatus} userDismissList = ${this.userDismissList} `, true);
                    if (!isMaster) {
                        this.sendPopDialogContent(code.APPLICATIOM_FAIL, '已申请离开失败，不可重复申请', [uid]);
                    }
                    return code.CAN_NOT_LEAVE_ROOM;
                } else {
                    //游戏开始后 座位上的玩家不能离开
                    this.showLog(`[${uid}]game started playing user leave fail uid = ${uid} chairID = ${user.chairID} playStatus = ${user.playStatus} roomGameStarted = ${this.roomGameStarted} `, true);
                    if (!isMaster) {
                        this.sendPopDialogContent(code.CAN_NOT_LEAVE_ROOM, '游戏中不能离开房间', [uid]);
                    }
                    return code.CAN_NOT_LEAVE_ROOM;
                }
            } else {
                //游戏没开始 可以随便离开
                this.isCanStart = false; //游戏不能开始
                this.showLog(`[${uid}]user leave success uid = ${uid} chairID = ${user.chairID} playStatus = ${user.playStatus} roomGameStarted = ${this.roomGameStarted} `);
                let leaveMessage = roomProto.userLeaveRoomResponse(user.chairID, uid);
                this.sendRoomData(leaveMessage);
                this.userLeaveRoom(uid).catch((err) => { logger.error(new Error(err.stack)); });
                return code.OK;
            }

        } else {
            this.showLog(`[${uid}]user leave fail not in this room`, true);
            return code.CAN_NOT_LEAVE_ROOM;
        }
    }

    //用户获取场景消息
    async getRoomSceneInfo(uid) {

        this.showLog(`[${uid}]user get game scene info begin`);
        let user = this.userArr[uid];
        if (!user) {
            this.showLog(`[${uid}]user get game scene info fail user not in this room`, true);
            return;
        }

        if (this.roomDismissed) {
            //房间已经解散 
            await this.userLeaveRoom(uid).catch((err) => {
                logger.error(new Error(err.stack));
            });
            this.showLog(`[${uid}]user get game scene info fail room dismissed = ${this.roomDismissed}`, true);
            return;
        }

        this.changeOnlineStatus(uid, roomProto.userStatusEnum.NONE);

        // 向其他玩家推送进入房间的消息
        let roomUserInfo = {
            userInfo: user.userInfo,
            userStatus: user.userStatus,
            playStatus: user.playStatus,
            chairID: user.chairID,
            uid: uid,
        };

        //给其他玩家推送玩家进入房间消息
        let userEntryRoomPush = roomProto.otherUserEntryRoomPush(roomUserInfo, this.gameType);
        await this.sendRoomDataExceptUid(userEntryRoomPush, [uid]);

        let gameData = this.getEnterGameData(uid);
        let userInfoArr = this.getUserInfoArr();
        await this.sendRoomData(roomProto.getRoomSceneInfoPush(this.roomID, this.creatorInfo, this.gameRule, userInfoArr, gameData), [uid]);
        if (this.askForExitArr.length > 0) {
            this.askForDismiss(uid, null).catch(err => {
                logger.error(err.stack)
            });
        }
        this.showLog(`[${uid}]user get game scene info success`);

    }

    //获取玩家对象
    getUserInfoArr() {

        let userInfoArr = [];
        for (let key in this.userArr) {
            let curUser = this.userArr[key];
            if (curUser) {
                curUser.userInfo.score = parseFloat(curUser.userInfo.score.toFixed(2));
                userInfoArr.push({
                    uid: curUser.userInfo.uid,
                    userInfo: curUser.userInfo,
                    chairID: curUser.chairID,
                    userStatus: curUser.userStatus,
                    playStatus: curUser.playStatus,
                });
            }
        }
        return userInfoArr;
    }

    //玩家离开房间
    async userLeaveRoom(uid, isSendMessage) {

        this.showLog(`[${uid}]user leave room begin`);
        let user = this.userArr[uid] || null;
        if (!user) {
            //玩家不在房间中时 清理玩家的房间数据
            await dao.updateData('userModel', { uid: uid, roomID: this.roomID }, { roomID: '' });
            return;
        }
        //将玩家踢出房间
        await this.kickUser(uid, isSendMessage);
        delete this.userDismissList[uid]; //玩家离座请求
        this.isCanStart = true;
        this.showLog(`[${uid}]user leave room end game can start isCanStart = ${this.isCanStart}`);
        //判断房间是否解散
        // await this.checkIsCanDismissRoom(uid);

    }

    //踢出用户
    async kickUser(uid, isSendMessage = true) {

        this.showLog(`[${uid}]room kick user begin`);
        let user = this.userArr[uid] || null;
        if (user) {

            let userRoomInfo = { userInfo: user.userInfo, chairID: user.chairID, uid: uid, roomID: this.roomID };
            let leaveMessage = roomProto.userLeaveRoomPush(userRoomInfo);
            let curUser = await userDao.getUserDataByUid(uid);
            //在当前房间中
            if (curUser.roomID && curUser.roomID.toString() == this.roomID.toString()) {
                await this.userLeaveRoomNotify([{ uid: uid, sid: user.userInfo.frontendId }], this.roomID).catch(err => { logger.error(err.stack) });
                if (isSendMessage) {
                    await this.sendRoomData(leaveMessage);
                } else {
                    //不给玩家发送离开消息
                    await this.sendRoomDataExceptUid(leaveMessage, [uid]);
                }
                delete this.userArr[uid];
                delete this.isCanNotReady[uid];
                let playUserList = this.getPlayingUserList();
                let allUserList = this.getAllUserList();
                this.showLog(`[${uid}]room kick user end playUserList = ${playUserList} allUserList = ${allUserList}`);
            } else {
                this.showLog(`[${uid}]kick user not in this room`, true);
                this.sendRoomDataExceptUid(leaveMessage, [uid]);
            }

        } else {
            // this.showLog(`[${ uid }]room kick user fail user not in this room`, true);
            return;
        }
    }

    //玩家离开房间信息更改

    async userLeaveRoomNotify(uidAndSidArr, roomID) {

        for (let i = 0; i < uidAndSidArr.length; ++i) {

            let uidAndSid = uidAndSidArr[i];
            let curUser = await userDao.getUserDataByUid(uidAndSid.uid);
            //20241101信息一致才清除
            if (curUser && curUser.roomID && curUser.roomID.toString() == roomID.toString()) {
                let updateUserData = { roomID: "" };
                await userDao.updateUserDataByUid(uidAndSid.uid, updateUserData);
                if (!uidAndSid.sid) continue;
                let session = await pomeloServices.getSession(uidAndSid.sid, uidAndSid.uid);
                if (!session) return;
                await pomeloServices.pushSessionData(session, "roomID", null);
                await userInfoServices.updateUserDataNotify(uidAndSid.uid, uidAndSid.sid, { roomID: "" }).catch((err) => { logger.error(err.stack) });
                this.showLog(`[${uidAndSid.uid}]user leave room and clear user room information frontID = ${uidAndSid.sid} `);
            }
        }
    }

    //玩家中途坐下
    async userChangeSeat(uid, fromChairID, toChairID) {

        this.showLog(`[${uid}]user sit down begin fromChairID = ${fromChairID} toChairID = ${toChairID} `);
        //基础条件判断
        if (typeof toChairID !== 'number' || toChairID >= this.chairCount || fromChairID < this.chairCount) return;

        //不是旁观状态
        let user = this.userArr[uid];
        if (!user || user.playStatus != roomProto.userPlayEnum.WATCH) {
            this.showLog(`[${uid}]user sit down fail playStatus = ${user && user.playStatus} userChairID = ${user && user.chairID} `, true);
            return;
        }

        if (this.gameType == enumeration.gameType.CNJJ && !this.checkUserLeave(uid)) {
            this.showLog(`[${uid}] cnjj can not sit playStatus = ${user && user.playStatus} userChairID = ${user && user.chairID} `, true);
            return;
        }

        // 目标位置如果有人，也不能换座位
        let toUser = this.getUserByChairID(toChairID);
        if (!!toUser) {
            let userSitDownPush = roomProto.getUserChangeSeatPush(fromChairID, toChairID, uid, user.playStatus, code.ROOM_SEAT_NOT_EMPTY, [uid]);
            this.showLog(`[${uid}] user sit down fail chairID have player seatUserUid = ${toUser.userInfo.uid} toChairID = ${toChairID} `, true);
            await this.sendRoomData(userSitDownPush, [uid]);
            return;
        }

        //同时一个位置只能一个人坐
        let lockKey = this.roomID + '-' + toChairID;
        let isLocked = redisService.isLocked(lockKey, 3);
        //可以进入
        if (!isLocked) {

            let errCode = await this.checkEntryRoom(user.userInfo);
            if (errCode != code.OK) {
                await this.sendRoomData(roomProto.getUserChangeSeatPush(fromChairID, toChairID, uid, user.playStatus, errCode), [uid]);
                this.showLog(`[${uid}] user sit down fail checkEntryRoom errCode = ${errCode} `);
                redisService.unLock(lockKey);
                return;

            } else {

                //超时踢出
                if (!this.roomGameStarted && this.unionRoom) {
                    let unionData = await dao.findOneData('unionModel', { unionID: this.creatorInfo.unionID });
                    if (unionData.unReadyKick) {
                        this.gameRule.unReadyKick = unionData.unReadyKick;
                    }
                    if (this.gameRule.unReadyKick) {
                        this.addKickScheduleEvent(user.userInfo.uid, this.gameRule.unReadyKick);
                    }
                }
                this.userArr[uid].chairID = toChairID;
                this.changePlayStatus(uid, roomProto.userPlayEnum.NONE);
                this.showLog(`[${uid}] user sit down to chairID = ${toChairID} success`);
                await this.sendRoomData(roomProto.getUserChangeSeatPush(fromChairID, toChairID, uid, user.playStatus, code.OK));
                redisService.unLock(lockKey);
                return;
            }

        } else {
            //此房间当前位置被锁定了 提示位置有人
            await this.sendRoomData(roomProto.getUserChangeSeatPush(fromChairID, toChairID, uid, user.userStatus, code.ROOM_SEAT_NOT_EMPTY), [uid]);
            this.showLog(`[${uid}] user sit down fail sit locked`, true);
            return;
        }
    }


    //玩家离线
    async userOffLine(uid) {

        let user = this.userArr[uid] || null;
        this.showLog(`[${uid}] user offline begin`);
        if (user) {
            //更改状态为离线状态
            this.changeOnlineStatus(uid, roomProto.userStatusEnum.OFFLINE);
            await this.sendRoomData(roomProto.userOffLinePush(user.chairID, uid));
        } else {
            this.showLog(`[${uid}] user offline end user not in room`);
            return;
        }
    }

    //玩家聊天
    userChat(uid, type, msg, recipientID) {

        let user = this.userArr[uid];
        let lockKey = uid + 'SendChat';
        let isLocked = redisService.isLocked(lockKey, 2);
        if (!user || isLocked) {
            return;
        }
        let chairID = user.chairID;
        this.sendRoomData(roomProto.userChatPush(chairID, uid, type, msg, recipientID));
    }

    //赠送礼物
    userSendGift(uid, toChairID, giftID) {

        let user = this.userArr[uid];
        if (user.chairID == toChairID) return;
        let lockKey = uid + 'SendGift';
        let isLocked = redisService.isLocked(lockKey, 2);
        if (!user || isLocked) {
            return;
        }
        let fromChairID = user.chairID;
        this.sendRoomData(roomProto.getUserGiftSendPush(fromChairID, toChairID, giftID));
    }

    /*-------------------------业务逻辑基础方法---------------------------------*/
    /*
     * 玩家邀请
     */
    async gameInvitation(uid, data) {
        let user = this.userArr[uid].userInfo;
        //组合邀请人数据
        let inviter = {
            uid: user.uid,
            avatar: user.avatar,
            nickname: utils.getNickName(user.nickname),
            roomID: user.roomID,
            frontendId: user.frontendId,
        };
        //if (user.chairID == toChairID) return;
        let lockKey = uid + 'SendGift';
        let isLocked = redisService.isLocked(lockKey, 10);
        if (!user || isLocked) {
            return;
        }
        if (!isLocked) {
            let nextunionID = data.unionID; //当前玩家联盟ID
            let nextroomID = data.roomID; //房间ID
            for (let key in data.uid) {
                let nextuid = data.uid[key]; //收到的玩家UID
                let unionData = await dao.findOneData('userModel', {
                    'uid': nextuid
                });
                //玩家不在线
                if (unionData.frontendId == '') {
                    return this.sendRoomData(roomProto.gameInvitationResult("玩家不在线"), [uid]);
                }
                //玩家已开始游戏
                if (unionData.roomID != '') {
                    return this.sendRoomData(roomProto.gameInvitationResult("玩家已开始游戏"), [uid]);
                }
                await pushAPI.gameInvitation({
                    nextunionID,
                    nextroomID,
                    inviter,
                    gameRule: this.gameRule,
                }, [{
                    uid: nextuid,
                    sid: unionData.frontendId
                }]);
            }
            let message = roomProto.gameInvitationResult("发送邀请成功");
            await this.sendRoomData(message, [uid]);
            return;
            //this.sendRoomData(roomProto.gameInvitationPush(data.unionID, uid, data.roomID), [data.uid]);
        }
    };

    //用户进入到房间场景里面
    async userJoinToScene(userInfo, chairID, playStatus) {

        //可以加入房间 构建用户信息
        userInfo.score = parseFloat(userInfo.score.toFixed(2));
        let user = {
            uid: userInfo.uid,
            userInfo: userInfo,
            chairID: chairID,
            playStatus: playStatus,
            userStatus: roomProto.userStatusEnum.NONE,
        };

        //保存用户到列表
        this.userArr[userInfo.uid] = user;

        // 更新用户信息
        let newUserData = await userDao.updateUserDataByUid(userInfo.uid, { roomID: this.roomID });
        // 修改房间用户session信息
        if (!userInfo.isRobot) {
            //推送玩家房间号信息
            await userInfoServices.updateUserDataNotify(newUserData.uid, newUserData.frontendId, {
                roomID: this.roomID
            }).catch(err => { });
            let session = await pomeloServices.getSession(userInfo.frontendId, userInfo.uid);
            if (!!session) {
                await pomeloServices.pushSessionData(session, 'roomID', this.roomID);
            } else {
                this.showLog(`[${userInfo.uid}] session is null`, true);
                return code.FAIL;
            }
        }

        // 向其他玩家推送进入房间的消息
        let roomUserInfo = {
            userInfo: user.userInfo,
            userStatus: user.userStatus,
            playStatus: user.playStatus,
            chairID: user.chairID,
            uid: userInfo.uid,
        };

        //给其他玩家推送玩家进入房间消息
        let userEntryRoomPush = roomProto.otherUserEntryRoomPush(roomUserInfo, this.gameType);
        await this.sendRoomDataExceptUid(userEntryRoomPush, [roomUserInfo.uid]);
        // 推送玩家自己进入房间的消息
        this.sendSelfEntryRoomData(userInfo);
        this.showLog(`[${userInfo.uid}] user join room success chairID = ${roomUserInfo.chairID} playStatus = ${roomUserInfo.playStatus} userStatus = ${roomUserInfo.userStatus} `);
        return code.OK;

    }

    //玩家进入房间并坐下
    async userEntryRoom(userInfo) {

        this.showLog(`[${userInfo.uid}] user entry room  begin nickname = ${userInfo.nickname} `);
        if (this.roomDismissed) {
            this.showLog(`[${userInfo.uid}] room is dismissed  entry fail`);
            return code.ROOM_JOIN_FAIL;
        }
        let lockKey = this.roomID + '-Join-Room';
        let isLocked = redisService.isLocked(lockKey, 3);
        //未锁定可以进入
        if (!isLocked) {

            if (this.goldRoom) {
                userInfo.goldRoom = true;
            }
            // 将用户信息转化为对应俱乐部的信息
            if (this.unionRoom) {
                userInfo = userInfoServices.buildGameRoomUserInfoWithUnion(userInfo, this.creatorInfo.unionID);
            } else {
                userInfo = userInfoServices.buildGameRoomUserInfoWithUnion(userInfo, 1);
            }

            let chairID = this.getRoomChairID(userInfo.uid);
            if (chairID >= this.chairCount) {
                this.showLog(`[${userInfo.uid}] room user is full chairID = ${chairID} `);
                redisService.unLock(lockKey);
                return code.ROOM_PLAYER_COUNT_FULL;
            }

            let chairLock = this.roomID + '-' + chairID;
            let isLocked = redisService.isLocked(chairLock, 3);
            if (isLocked) {
                redisService.unLock(lockKey);
                return code.ROOM_JOIN_BUSY_LIMIT;
            }

            //是否可以加入游戏
            let checkJoinCode = await this.checkEntryRoom(userInfo);
            if (checkJoinCode != code.OK) {
                redisService.unLock(lockKey);
                redisService.unLock(chairLock);
                return checkJoinCode;
            }

            //是否进到场景里面
            let userPlayStatus = roomProto.userPlayEnum.NONE;
            if (this.userArr[userInfo.uid] && this.userArr[userInfo.uid].playStatus != userPlayStatus) {
                userPlayStatus = this.userArr[userInfo.uid].playStatus;
            }
            let isJoinSuccess = await this.userJoinToScene(userInfo, chairID, userPlayStatus);
            let resCode = isJoinSuccess ? isJoinSuccess : code.OK;

            if (this.unionRoom) {
                let unionData = await dao.findOneData('unionModel', {
                    unionID: this.creatorInfo.unionID
                });
                if (unionData.unReadyKick) {
                    this.gameRule.unReadyKick = unionData.unReadyKick;
                }
            }

            if (resCode == code.OK && this.gameRule.unReadyKick) {
                this.addKickScheduleEvent(userInfo.uid, this.gameRule.unReadyKick);
            }
            redisService.unLock(lockKey);
            redisService.unLock(chairLock);
            return resCode;

        } else {

            this.showLog(`[${userInfo.uid}] join room is locked entry fail`, true);
            return code.ROOM_JOIN_BUSY_LIMIT;
        }
    }

    //玩家进入房间并旁观
    async userJoinWatch(userInfo) {
        //注释掉 可以通过
        //this.showLog(`[${ userInfo.uid }] user join watch begin nickname = ${ userInfo.nickname } `);
        if (this.roomDismissed) {
            this.showLog(`[${userInfo.uid}] room is dismissed join fail`);
            return code.ROOM_JOIN_FAIL;
        }
        if (this.userArr[userInfo.uid] && this.userArr[userInfo.uid].chairID < this.chairCount) {
            this.userEntryRoom(userInfo);
            return;
        }
        let isCanWatch = this.gameRule.canWatch;
        if (isCanWatch) {

            // 将用户信息转化为对应俱乐部的信息
            if (this.creatorInfo.creatorType === enumeration.roomCreatorType.UNION_CREATE) {
                userInfo = userInfoServices.buildGameRoomUserInfoWithUnion(userInfo, this.creatorInfo.unionID);
            } else {
                userInfo = userInfoServices.buildGameRoomUserInfoWithUnion(userInfo, 1);
            }

            let chairID = this.chairCount + utils.getLength(this.userArr);
            //是否进到场景里面
            let isJoinSuccess = await this.userJoinToScene(userInfo, chairID, roomProto.userPlayEnum.WATCH);
            let resCode = isJoinSuccess ? isJoinSuccess : code.OK;
            return resCode;

        } else {
            this.showLog(`[${userInfo.uid}] room not allow watch`, true);
            return code.ROOM_NOT_WATCH;
        }

    }

    //获取房间信息
    getRoomInfo() {
        if (this.roomDismissed) return null;
        let roomUserInfoArr = [];
        let playerCount = 0;
        for (let key in this.userArr) {
            if (this.userArr.hasOwnProperty(key)) {
                let user = this.userArr[key];
                if (user.chairID < this.chairCount) {
                    playerCount += 1;
                }
                roomUserInfoArr.push({
                    avatar: user.userInfo.avatar,
                    uid: user.userInfo.uid,
                    chairID: user.chairID,
                    nickname: user.userInfo.nickname,
                    winScore: user.winScore || 0
                });
            }
        }
        let roomInfo = {
            curBureau: this.curBureau,
            chairCount: this.chairCount,
            playerCount: playerCount,
            roomID: this.roomID,
            gameRule: this.gameRule,
            maxBureau: this.maxBureau,
            curBureau: this.curBureau,
            gameStarted: this.roomGameStarted,
            roomUserInfoArr: roomUserInfoArr
        }
        return roomInfo;
    }

    //获取房间座位号
    getRoomChairID(uid) {

        let user = this.getUserByUID(uid);
        if (user) {
            return user.chairID
        } else {
            for (let i = 0; i < this.chairCount; i++) {
                let user = this.getUserByChairID(i);
                if (!user) {
                    return i;
                }
            }
            return this.chairCount;
        }
    }

    //检查玩家可以加入房间
    async checkEntryRoom(userInfo) {

        this.showLog(`[${userInfo.uid}] check user join room condition begin`);
        if (!this.unionRoom) {

            if (this.goldRoom) {
                //如果是金币场 检测分数是否足够
                let curScore = userInfo.score;
                if (curScore < this.gameRule.scoreLowLimit) {
                    this.showLog(`[${userInfo.uid}] not enough gold = ${curScore} limitScore = ${this.gameRule.scoreLowLimit} `, true);
                    return code.NOT_ENOUGH_SCORE;
                }
                return code.OK;
            }
            else {
                //普通房检查钻石是否足够
                if (this.gameRule.payType === enumeration.roomPayType.WOZHIFU) {
                    if (userInfo.uid === this.creatorInfo.uid) {
                        let payDiamond = gameConfig.oneUserDiamondCount(this.gameRule.bureau, this.gameRule.gameType, this.chairCount);
                        if (userInfo.gold < payDiamond) {
                            return code.NOT_ENOUGH_GOLD;
                        }
                    }
                } else if (this.gameRule.payType === enumeration.roomPayType.YINGJIAZHIFU) {
                    //赢家支付
                    let payDiamond = gameConfig.oneUserDiamondCount(this.gameRule.bureau, this.gameRule.gameType, this.chairCount);
                    if (userInfo.gold < payDiamond) {
                        return code.NOT_ENOUGH_GOLD;
                    }
                }
                else if (this.gameRule.payType === enumeration.roomPayType.AAZHIFU) {
                    //AA支付
                    let payDiamond = this.gameRule.bureau;
                    if (userInfo.gold < payDiamond) {
                        return code.NOT_ENOUGH_GOLD;
                    }
                }
            }

        } else {

            //钻石判断
            {
                if (this.gameRule.payType === enumeration.roomPayType.AAZHIFU) {
                    //AA支付
                    let payDiamond = this.gameRule.bureau;
                    if (userInfo.gold < payDiamond) {
                        return code.NOT_ENOUGH_GOLD;
                    }
                }
                else if (this.gameRule.payType === enumeration.roomPayType.YINGJIAZHIFU) {
                    //赢加支付
                    let payDiamond = gameConfig.oneUserDiamondCount(this.gameRule.bureau, this.gameRule.gameType, this.chairCount);
                    if (userInfo.gold < payDiamond) {
                        return code.NOT_ENOUGH_GOLD;
                    }
                } else {

                    //圈主支付
                    let curOwner = await userDao.getUserDataByUid(this.creatorInfo.ownerUid);
                    let payDiamond = gameConfig.oneUserDiamondCount(this.gameRule.bureau, this.gameRule.gameType, this.chairCount);
                    if (!curOwner || curOwner.gold < payDiamond) {
                        return code.NOT_ENOUGH_GOLD;
                    }
                }
            }

            //俱乐部房间检测是否被禁止加入游戏
            if (!!userInfo.prohibitGame) {
                this.showLog(`[${userInfo.uid}]prohibitGame = ${userInfo.prohibitGame} `, true);
                return code.PERMISSION_NOT_ENOUGH;
            }

            //校验最后三局是否可以加入
            if (this.gameRule.lastThreeJoin && this.gameRule.bureau > 3 && this.gameRule.bureau - this.curBureau < 3) {
                //最后三局不能加入
                this.showLog(`[${userInfo.uid}] can not join in last three bureau = ${this.gameRule.bureau} curBureau = ${this.curBureau} `, true);
                return code.THE_FINAL_THREE_MATCHES;
            }
        }

        // 防作弊模式，计算玩家距离，任意玩家距离差不能低于100米
        if (!!this.gameRule.fangzuobi) {
            if (!userInfo.address || userInfo.address.length === 0) {
                this.showLog(`[${userInfo.uid}] can not jion not location`, true);
                return code.CAN_NOT_ENTER_NOT_LOCATION;
            }
            let location = JSON.parse(userInfo.address);
            for (let key in this.userArr) {
                if (this.userArr.hasOwnProperty(key)) {
                    if (userInfo.uid != key) {
                        let distanceLimit = (parseInt(this.gameRule.distanceLimit) / 1000);
                        if (utils.getDistanceByLocation(location, JSON.parse(this.userArr[key].userInfo.address)) < distanceLimit) {
                            this.showLog(`[${userInfo.uid}] can not jion  game too near`, true);
                            return code.CAN_NOT_ENTER_TOO_NEAR;
                        }
                    }
                    if (userInfo.lastLoginIP == this.userArr[key].userInfo.lastLoginIP) {
                        return code.CAN_NOT_ENTER_TOO_NEAR;
                    }
                }
            }
        }

        this.showLog(`[${userInfo.uid}] check user join room condition success`);
        return code.OK;
    }


    //获取支付的钻石
    getRoomPayDiamond() {
        let payDiamond = gameConfig.oneUserDiamondCount(this.gameRule.bureau, this.gameRule.gameType, this.chairCount);
        return payDiamond;
    }


    getResultDiamond() {

        if (this.gameRule.payType == enumeration.roomPayType.AAZHIFU) {
            return this.gameRule.bureau
        } else {
            return this.getRoomPayDiamond();
        }
    }


    //房间游戏开始
    async startGame() {

        //游戏状态不对
        if (!this.gameStarted) {
            this.showLog(`start game fail game already started = ${this.gameStarted} `, true);
            return;
        }

        // 判断联盟是否可以游戏
        if (this.maxBureau > 0 && this.curBureau === 0 && this.unionRoom) {
            let union = await pomelo.app.unionManager.getUnion(this.creatorInfo.unionID);
            if (union && !union.isOpening()) {
                this.sendPopDialogContentToAll(null, "联盟已打烊，无法开始新的牌局");
                await this.dismissRoom(enumeration.gameRoomDismissReason.UNION_OWNER_DISMISS);
                this.gameStarted = false;
                this.roomGameStarted = false;
                return;
            }
        }

        let playingUser = this.getPlayingUserList();
        //金币支付
        if (this.gameRule.payType == enumeration.roomPayType.GOLD) {
            for (let i = 0; i < playingUser.length; i++) {
                let uid = playingUser[i];
                if (!this.payTicketUser.includes(uid)) {
                    let type = enumeration.scoreChangeType.TICKET;
                    let score = - (this.gameRule.baseScore);
                    let curUser = this.getUserByUID(uid);
                    let detail = { score, type, gameType: this.gameType };
                    await userInfoServices.addGoldScore(curUser, detail);
                    this.userArr[uid].userInfo.score += score;
                    this.payTicketUser.push(uid);
                    //更新玩家分数推送
                    let totalScore = this.userArr[uid].userInfo.score;
                    await this.updateRoomUserInfo({ uid: uid, score: totalScore });
                }
            }
        }

        // 第一局游戏开局时收取房费
        if (this.curBureau == 0 && this.gameRule.payType == enumeration.roomPayType.WOZHIFU) {
            //房主支付或者圈主支付
            let payResult = await this.payRoomCost();
            if (payResult != code.OK) {
                // 收取房费失败，解散房间
                this.sendPopDialogContent(null, "钻石不足,请退出房间！");
                if (!this.unionRoom) {
                    //普通场解散房间
                    await this.dismissRoom(enumeration.gameRoomDismissReason.UNION_OWNER_DISMISS);
                }
                this.gameStarted = false;
                this.roomGameStarted = false;
                return;
            }
        }

        //如果是AA支付收取砖石
        if (this.gameRule.payType == enumeration.roomPayType.AAZHIFU) {
            for (let i = 0; i < playingUser.length; i++) {
                let uid = playingUser[i];
                let count = 1;
                this.userPayCost(uid, count);
            }
        }

        //人数不足解散游戏
        if (playingUser.length <= 1) {
            this.showLog(`low user count = ${playingUser.length} and room dissmiss`);
            this.sendPopDialogContent(null, "玩家人数不足，房间已解散！");
            await this.dismissRoom(enumeration.gameRoomDismissReason.UNION_OWNER_DISMISS);
            this.gameStarted = false;
            this.roomGameStarted = false;
            return;
        } else {
            //子游戏开始
            if (!this.roomDismissed && this.curBureau < this.maxBureau) {
                this.onEventGameStart();
            }
        }
    }

    //更改玩家在线离线状态
    changeOnlineStatus(uid, status) {

        let user = this.userArr[uid];
        if (user) {
            this.showLog(`[${uid}] change userStatus = ${this.userArr[uid].userStatus} to newStatus = ${status} `);
            this.userArr[uid].userStatus = status;
        }
    }

    //更新玩家游戏状态
    changePlayStatus(uid, status) {
        let user = this.userArr[uid];
        if (user) {
            let beforeStatus = this.userArr[uid].playStatus;
            this.showLog(`[${uid}] change user playStatus = ${beforeStatus} to newStatus = ${status} `);
            this.userArr[uid].playStatus = status;
        }
        //推送所有玩家状态信息
        let userPlayStatus = this.getAllUserPlayStatus();
        this.sendRoomData(roomProto.getUserPlayeStatusPush(userPlayStatus));
    }

    //获取所有玩家的游戏状态
    getAllUserPlayStatus() {
        let userPlayStatus = {}
        for (let key in this.userArr) {
            let user = this.userArr[key];
            if (user) {
                userPlayStatus[key] = user.playStatus
            }
        }
        return userPlayStatus;
    }

    //检查是否解散房间
    async checkIsCanDismissRoom(uid) {

        if (this.roomDismissed) return false;
        let list = [];
        for (let key in this.userArr) {
            let user = this.userArr[key];
            if (user) {
                if (!list.includes(key)) {
                    list.push(key);
                }
            }
        }
        let isCanDismiss = list.length === 0;
        this.showLog(`[${uid}] check room dissmiss userCount = ${list.length} isCanDismiss = ${isCanDismiss} `);
        if (isCanDismiss) {
            await this.dismissRoom(enumeration.gameRoomDismissReason.NONE);
        }
    }

    //获取已准备用户
    getReadyUserList() {
        let list = [];
        for (let key in this.userArr) {
            let user = this.userArr[key];
            if (user && user.chairID < this.chairCount && user.playStatus == roomProto.userPlayEnum.READY) {
                if (!list.includes(key)) {
                    list.push(key);
                }
            }
        }
        return list;
    }

    //获取座位号最小的玩家
    getFirstUserID() {
        let list = [];
        for (let key in this.userArr) {
            let user = this.userArr[key];
            if (user && user.chairID < this.chairCount && user.playStatus == roomProto.userPlayEnum.PLAYING) {
                list.push({
                    chairID: user.chairID,
                    uid: key
                });
            }
        }
        list.sort((a, b) => {
            return a.chairID - b.chairID
        });
        return list[0].uid;
    }

    //获取当前房间的玩家
    getGameUserList() {
        let list = [];
        for (let key in this.userArr) {
            let user = this.userArr[key];
            if (user && user.chairID < this.chairCount) {
                if (!list.includes(key)) {
                    list.push(key);
                }
            }
        }
        return list;
    }

    //获取按照庄家牌型的玩家列表
    getPlayingUserBySeat(bankerUid) {

        let curUid = this.getNextUserID(bankerUid)
        let userList = [];
        while (curUid && curUid != bankerUid) {
            userList.push(curUid);
            curUid = this.getNextUserID(curUid);
        }
        return userList;
    }

    //获取当前房间正在游戏的玩家
    getPlayingUserList() {
        let list = [];
        for (let key in this.userArr) {
            let user = this.userArr[key];
            if (user && user.chairID < this.chairCount && user.playStatus == roomProto.userPlayEnum.PLAYING) {
                if (!list.includes(key)) {
                    list.push(key);
                }
            }
        }
        return list;
    }

    getRobotUserList() {
        let list = [];
        for (let key in this.userArr) {
            let user = this.userArr[key];
            if (user && user.userInfo.isRobot && user.chairID < this.chairCount && user.playStatus != roomProto.userPlayEnum.WATCH) {
                if (!list.includes(key)) {
                    list.push(key);
                }
            }
        }
        return list;
    }

    getWatchUserList() {

        let list = [];
        for (let key in this.userArr) {
            let user = this.userArr[key];
            if (user && user.chairID >= this.chairCount && user.playStatus == roomProto.userPlayEnum.WATCH) {
                if (!list.includes(key)) {
                    list.push(key);
                }
            }
        }
        return list;
    }

    getOnSeatUser() {

        let list = [];
        for (let key in this.userArr) {
            let user = this.userArr[key];
            if (user && user.chairID < this.chairCount) {
                if (!list.includes(key)) {
                    list.push(key);
                }
            }
        }
        return list;
    }


    //获取等待的玩家
    getWaitingUserList() {

        let list = [];
        for (let key in this.userArr) {
            let user = this.userArr[key];
            if (user && user.chairID < this.chairCount && user.playStatus == roomProto.userPlayEnum.NONE) {
                if (!list.includes(key)) {
                    list.push(key);
                }
            }
        }
        return list;
    }


    //获取所有玩家
    getAllUserList() {
        let list = [];
        for (let key in this.userArr) {
            let user = this.userArr[key];
            if (user) {
                if (!list.includes(key)) {
                    list.push(key);
                }
            }
        }
        return list;
    }

    //支付房费
    async payRoomCost() {

        if (!this.unionRoom) {
            //普通场支付
            if (this.curBureau == 0) {
                // return await gameDataService.roomPay(this.userArr, this.gameRule, this.chairCount, this.roomID, this.creatorInfo.uid, this.gameType);
                let payDiamond = gameConfig.oneUserDiamondCount(this.gameRule.bureau, this.gameRule.gameType, this.chairCount);
                this.payDiamondUser[this.creatorInfo.uid] = (this.payDiamondUser[this.creatorInfo.uid] || 0) + payDiamond;
                return await gameDataService.userPay(this.creatorInfo.uid, payDiamond, this.roomID, this.creatorInfo.unionID || -1, this.gameType);
            } else {
                return code.OK;
            }
        } else {
            if (this.curBureau == 0) {
                let union = await pomelo.app.unionManager.getUnion(this.creatorInfo.unionID);
                let unionOwnerUid = union.getOwnerUid();
                let payDiamond = gameConfig.oneUserDiamondCount(this.gameRule.bureau, this.gameRule.gameType, this.chairCount);
                this.payDiamondUser[unionOwnerUid] = (this.payDiamondUser[unionOwnerUid] || 0) + payDiamond;
                return await gameDataService.unionRoomPay(unionOwnerUid, this.gameRule, this.roomID, this.creatorInfo.unionID, this.gameType);
            } else {
                return code.OK;
            }
        }
    }

    //玩家支付洗牌
    async userPayWash(uid, baseCount = 0, count = 1) {

        let matchData = { uid: uid, gold: { $gte: baseCount + count } };
        let curUser = await userDao.updateUserData(matchData, { $inc: { gold: -count } });
        if (curUser && !!curUser.frontendId) {
            userInfoServices.updateUserDataNotify(curUser.uid, curUser.frontendId, { gold: curUser.gold }).catch((err) => { logger.error(err.stack) });
        }
        return curUser;

    }

    //玩家支付
    async userPayCost(uid, count = 1) {

        let payDiamond = count;
        this.payDiamondUser[uid] = (this.payDiamondUser[uid] || 0) + payDiamond;
        return await gameDataService.userPay(uid, payDiamond, this.roomID, this.creatorInfo.unionID || -1, this.gameType);
    }

    //通过uid查找用户
    getUserByUID(uid) {
        let user = this.userArr[uid];
        if (user) {
            return this.userArr[uid];
        }
        return null;
    }

    async getOwnerInfo(uid) {

        let userData = await userDao.getUserDataByUid(uid);
        let userInfo = {};
        if (userData) {
            let { uid, nickname, avatar } = userData;
            userInfo = { uid, nickname, avatar }
        }
        return { userInfo, uid };
    }

    //通过座位号查找用户
    getUserByChairID(chairID) {
        let userArr = this.userArr;
        for (let key in userArr) {
            if (userArr.hasOwnProperty(key) && (userArr[key].chairID === chairID)) {
                return userArr[key];
            }
        }
        return null;
    }

    //重置玩家状态
    async resetPlayerStatus(status = roomProto.userPlayEnum.NONE) {
        for (let key in this.userArr) {
            if (this.userArr.hasOwnProperty(key)) {
                let user = this.userArr[key];
                if (user && user.chairID < this.chairCount && user.playStatus != roomProto.userPlayEnum.WATCH) {
                    this.changePlayStatus(key, status);
                }
            }
        }
    }

    //判断用户是否在房间
    isUserInRoom(uid) {
        if (this.roomDismissed) return false;
        return !!this.userArr[uid];
    }


    //记录单局游戏分数变化及记录
    async recordGameResult(dataArr) {

        if (!dataArr || dataArr.length === 0) return;
        for (let i = 0; i < dataArr.length; ++i) {
            let data = dataArr[i];
            let user = this.userArr[data.uid];
            let cloneUser = this.cloneObject(user);
            if (!user) continue;
            let score = data.score || 0;
            let beforScore = user.userInfo.score;
            let totalScore = parseFloat(user.userInfo.score + score);
            user.userInfo.score = totalScore;
            user.winScore = (user.winScore || 0) + score;
            this.showLog(`[${data.uid}] game score change beforScore = ${beforScore} score = ${score} endScore = ${totalScore} `);

            //保存金币变化
            if (this.goldRoom) {
                let curScore = (score < 0 && beforScore + score < 0) ? -beforScore : score;
                let detail = { type: enumeration.scoreChangeType.GAMEWIN, score: curScore, gameType: this.gameType };
                await userInfoServices.addGoldScore(cloneUser, detail);
            } else {

                let unionID = this.unionRoom ? this.creatorInfo.unionID : 1;
                let userType = await userInfoServices.checkUserAward(data.uid, totalScore, score, beforScore, this.gameType, unionID, this.userArr[data.uid].userInfo.userType);
                if (userType >= 0) {
                    this.userArr[data.uid].userInfo.userType = userType;
                }
            }

            //更新玩家分数推送
            await this.updateRoomUserInfo({ uid: data.uid, score: totalScore });
        }
    }






    //检测砖石是否足够
    async checkPayDiamond() {

        if (this.gameRule.payType == enumeration.roomPayType.AAZHIFU) {
            for (let key in this.userArr) {
                if (this.userArr.hasOwnProperty(key)) {
                    let user = this.userArr[key];
                    if (user.chairID >= this.chairCount || user.playStatus == roomProto.userPlayEnum.WATCH) continue;
                    let curUser = await userDao.getUserDataByUid(user.userInfo.uid);
                    let payDiamond = (this.gameRule.perDiamond || this.gameRule.payDiamond) || 1;
                    if (curUser.gold < payDiamond) {
                        this.showLog(`[${user.userInfo.uid}] have no gold userGold = ${curUser.gold} perDiamond = ${this.gameRule.perDiamond}  must be kick`);
                        await this.kickUser(user.userInfo.uid);
                    }
                }
            }
        }
    }



    //更新房间玩家信息
    async updateRoomUserInfo(newUserInfo, notify = true) {

        let user = this.userArr[newUserInfo.uid];
        if (!user) return;
        // 更新用户信息
        for (let key in newUserInfo) {
            if (newUserInfo.hasOwnProperty(key) && user.userInfo.hasOwnProperty(key) && key !== 'uid') {
                if (key == 'score') {
                    user.userInfo[key] = parseFloat(newUserInfo[key].toFixed(2));
                } else {
                    user.userInfo[key] = newUserInfo[key];
                }
            }
        }
        if (!!notify) await this.sendRoomData(roomProto.userInfoChangePush(user.userInfo));
    }


    //结算后更新所有玩家基础信息
    async notifyUpdateAllUserInfo() {

        // 更新所有玩家正在玩的玩家的分数变化
        for (let key in this.userArr) {
            let user = this.userArr[key];
            if (!user) continue;
            if (user.chairID < this.chairCount && user.playStatus == roomProto.userPlayEnum.PLAYING) {
                //正在游戏的玩家更新信息推送
                await this.sendRoomData(roomProto.userInfoChangePush(user.userInfo));
            }
        }
    }

    //每小局游戏结束相关
    async concludeGame() {

        // this.showLog(`game end gameStarted = ${ this.gameStarted } `);
        if (!this.gameStarted) return;
        this.gameStarted = false;
        //完成一局后可以保存记录
        this.isSaveRecords = true;
        //推送分数变化
        await this.sendRoomData(roomProto.roomUserWinTotalPush(this.userWinTotalScore));

        if (this.checkGoldRoomEnd() || (this.maxBureau > 0 && this.curBureau >= this.maxBureau)) {
            //游戏结束解散房间
            setTimeout(async () => { await this.dismissRoom(enumeration.gameRoomDismissReason.BUREAU_FINISHED); }, 1000)
            //延迟解散房间 等待大结算

        } else {
            // 通知更新所有玩家信息
            this.notifyUpdateAllUserInfo();
        }
    }

    //检测金币房是否解散房间
    checkGoldRoomEnd() {

        if (this.goldRoom) {

            let playingUser = this.getGameUserList();
            if (playingUser.length <= 1) {
                return true;
            }
            for (let i = 0; i < playingUser.length; i++) {
                let uid = playingUser[i];
                let user = this.getUserByUID(uid);
                let curScore = user.userInfo.score;
                if (curScore < 0 || curScore <= (this.gameRule.scoreDismissLimit + 1)) {
                    return true;
                }
            }
            return false;
        }
        return false;
    }

    //获取游戏录像数据
    getGameVideoData() {
        return this.gameVideoRecord.length > 0 ? this.gameVideoRecord : null;
    }

    //游戏回放数据
    getGameBureauData() {
        return this.reviewRecord;
    }

    //游戏总结算信息
    async onEventRoomDismiss() {

    }


    /*-------------------------待修改-------------------------------*/


    // 记录总局结果
    async recordAllDrawResult() {

        if (this.isSaveRecords) {
            this.showLog(`game total end and record all draw result`);
            // 记录录像
            let curGameVideoRecord = null;
            let gameVideoData = this.getGameVideoData();
            if (!!gameVideoData) {
                let saveData = {
                    roomID: this.roomID,
                    gameType: this.gameType,
                    detail: JSON.stringify(gameVideoData),
                    createTime: Date.now()
                };
                curGameVideoRecord = await dao.createData("gameVideoRecordModel", saveData);
            }


            //玩家详情
            let userList = [];
            let bigWinUid = null;
            let maxScore = 0;
            for (let key in this.userWinRecord) {
                let user = this.userWinRecord[key];
                if (user) {
                    let userScore = parseFloat(user.winScore) || 0;
                    if (userScore > maxScore) {
                        bigWinUid = key;
                        maxScore = userScore;
                    }
                    let myUser = this.getUserByUID(key);
                    let spreaderID = (myUser && myUser.userInfo.spreaderID) || null;
                    user.spreaderID = spreaderID;
                    userList.push(user);

                    //保存玩家每次输赢和战绩匹配
                    if (this.unionRoom) {
                        let dateTime = moment().format('YYYY-MM-DD');
                        let dayTime = moment().format('YYYYMMDD');
                        let saveTeamData = { $inc: { "score": userScore, "count": 1 }, dayTime: dayTime };
                        await dao.findOneAndUpdateEx('userScoreDataModel', { uid: key, unionID: this.creatorInfo.unionID, dateTime }, saveTeamData, { upsert: true, new: true });
                    }
                }
            }

            //大赢家支付钻石
            if (this.gameRule.payType == enumeration.roomPayType.YINGJIAZHIFU) {

                let payDiamond = gameConfig.oneUserDiamondCount(this.gameRule.bureau, this.gameRule.gameType, this.chairCount);
                this.showLog(`winner pay payDiamond = ${payDiamond} `);
                await this.userPayCost(bigWinUid, payDiamond);
            }

            //战绩保存
            {
                let detail = JSON.stringify(this.getGameBureauData() || []);
                let saveData = {
                    roomID: this.roomID,
                    gameType: this.gameType,
                    userList: userList,
                    detail: detail,
                    createTime: Date.now()
                };
                if (!!curGameVideoRecord) {
                    saveData.videoRecordID = curGameVideoRecord.videoRecordID;
                }
                if (this.unionRoom) {
                    saveData.unionID = this.creatorInfo.unionID;
                } else {
                    saveData.creatorUid = this.creatorInfo.uid
                }
                await dao.createData("userGameRecordModel", saveData).catch(e => { logger.error(e.stack) });
            }
        }

    }

    async dismissRoom(reason) {

        if (this.roomDismissed) return; //防止重复解散
        this.showLog(`room dissmiss begin reason = ${reason} `);
        this.roomDismissed = true;
        //先保存游戏记录
        if (this.curBureau >= 1) {
            //房间总结算
            await this.onEventRoomDismiss(reason);
            await this.recordAllDrawResult().catch((err) => { logger.error(err.stack); });
        }
        //发送消息
        await this.sendRoomData(roomProto.roomDismissPush(reason));
        this.showLog(`room dissmiss end reason = ${reason} `);

        //踢出玩家
        let isSendMessage = (reason == enumeration.gameRoomDismissReason.UNION_OWNER_DISMISS || reason == enumeration.gameRoomDismissReason.LIMIT_SCORE_DISMISS);
        //解散房间踢出用户
        for (let key in this.userArr) {
            if (this.userArr.hasOwnProperty(key)) {
                let user = this.userArr[key];
                if (user) {
                    await this.kickUser(key, isSendMessage);
                }
            }
        }
        //删除房间对象
        pomelo.app.unionManager.dismissRoom(this.roomID);

    }

    /* 正在解散中 */
    isDismissing() {
        return this.askForExitArr.length > 0;
    }

    destroyRoom(reason) {
        if (this.answerExitSchedule) {
            scheduler.cancelJob(this.answerExitSchedule);
        }
        // logger.error("roomFrame", "destroyRoom roomID:" + this.roomID);
        logger.debug(`destroy reason = ${reason} `);
    }

    //玩家请求解散房间
    async askForDismiss(uid, data) {

        // if (!this.roomGameStarted) return;  //游戏
        if (this.gameRule.canNotDissmiss) return; //游戏是否可以解散
        //如果是旁观玩家解散则直接离开房间
        let user = this.getUserByUID(uid);
        if (user && user.chairID >= this.chairCount) {
            this.userAskLeaveRoom(uid);
            return;
        }
        //如果是离线后解散
        if (!data) {
            //发送房间解散请求
            let curTime = this.askDissmissTime ? this.askDissmissTime.diff(moment(), 's') : 0;
            let message = roomProto.getAskForDismissPushData(this.askForExitArr, curTime);
            await this.sendRoomData(message, [uid]);
            return;
        }

        //游戏中的玩家请求解散
        let isAsker = data.isAsker || false;
        if (isAsker && this.askForExitArr.length == 0) {

            //判断解散次数
            if (this.unionRoom) {
                let unionData = await dao.findOneData('unionModel', {
                    unionID: this.creatorInfo.unionID
                });
                if (unionData && unionData.disApplyCount) {
                    this.gameRule.disApplyCount = unionData.disApplyCount;
                }
            }

            if (this.gameRule.disApplyCount) {

                if (this.askDissmissCount[uid] && this.askDissmissCount[uid] >= this.gameRule.disApplyCount) {
                    return;
                } else {
                    this.askDissmissCount[uid] = (this.askDissmissCount[uid] || 0) + 1;
                }
            }

            for (let key in this.userArr) {
                let user = this.userArr[key];
                if (user && user.userStatus != roomProto.userStatusEnum.WATCH && user.chairID < this.chairCount) {
                    let userInfo = {
                        uid: key,
                        nickname: user.userInfo.nickname,
                        userStatus: user.userStatus,
                        avatar: user.userInfo.avatar,
                        isExit: key == uid ? true : null,
                        isAsker: (key == uid),
                    }
                    this.askForExitArr.push(userInfo);
                }
            }

            //定时处理器
            let dismissTick = roomProto.EXIT_WAIT_SECOND;
            this.askDissmissTime = moment().add(dismissTick, 's');
            this.dismissRoomTimer = setTimeout(async () => {
                //超时直接解散
                this.askForExitArr = []; //清除请求状态
                this.askDissmissTime = null;
                await this.dismissRoom(enumeration.gameRoomDismissReason.USER_DISMISS);
            }, dismissTick * 1000);

            //发送房间解散请求
            let message = roomProto.getAskForDismissPushData(this.askForExitArr, dismissTick);
            await this.sendRoomData(message);

        } else if (this.askForExitArr.length > 0) {
            //其余玩家表示同意或者拒绝
            let isExit = data.isExit;
            let unionData = await dao.findOneData('unionModel', { unionID: this.creatorInfo.unionID });
            if (this.unionRoom && unionData && unionData.dismissType == 1) {
                this.askForExitArr = this.askForExitArr.map((item) => {
                    if (item.uid == uid) {
                        item.isExit = isExit;
                    }
                    return item;
                });

                let list = this.askForExitArr.filter(item => item.isExit == true);
                if (list.length * 2 > this.askForExitArr.length) {
                    if (this.dismissRoomTimer) {
                        clearTimeout(this.dismissRoomTimer);
                        this.dismissRoomTimer = null;
                    }
                    this.askForExitArr = []; //清除请求状态
                    this.askDissmissTime = null;
                }

                //发送房间解散请求
                let curTime = this.askDissmissTime ? this.askDissmissTime.diff(moment(), 's') : 0;
                let message = roomProto.getAskForDismissPushData(this.askForExitArr, curTime);
                await this.sendRoomData(message);

                if (this.askForExitArr.length == 0) {
                    await this.dismissRoom(enumeration.gameRoomDismissReason.USER_DISMISS);
                    return;
                }
                let list2 = this.askForExitArr.filter(item => item.isExit == false);
                if (list2.length * 2 >= this.askForExitArr.length) {
                    if (this.dismissRoomTimer) {
                        clearTimeout(this.dismissRoomTimer);
                        this.dismissRoomTimer = null;
                    }
                    this.askForExitArr = []; //清除请求状态
                    this.askDissmissTime = null;
                    let message = roomProto.getAskForDismissPushData(this.askForExitArr, 0);
                    await this.sendRoomData(message);
                }
            } else {

                //如果同意则更改状态
                if (isExit) {
                    this.askForExitArr = this.askForExitArr.map((item) => {
                        if (item.uid == uid) {
                            item.isExit = true;
                        }
                        return item;
                    });
                    //每一个都同意了
                    if (this.askForExitArr.every(item => item.isExit == true)) {
                        if (this.dismissRoomTimer) {
                            clearTimeout(this.dismissRoomTimer);
                            this.dismissRoomTimer = null;
                        }
                        this.askForExitArr = []; //清除请求状态
                        this.askDissmissTime = null;
                    }
                    //发送房间解散请求
                    let curTime = this.askDissmissTime ? this.askDissmissTime.diff(moment(), 's') : 0;
                    let message = roomProto.getAskForDismissPushData(this.askForExitArr, curTime);
                    await this.sendRoomData(message);
                    //如果所有人同意了
                    if (this.askForExitArr.length == 0) {
                        await this.dismissRoom(enumeration.gameRoomDismissReason.USER_DISMISS);
                    }

                } else {
                    //如果不同意 则不解散
                    if (this.dismissRoomTimer) {
                        clearTimeout(this.dismissRoomTimer);
                        this.dismissRoomTimer = null;
                    }
                    this.askForExitArr = []; //清除请求状态
                    this.askDissmissTime = null;
                    let message = roomProto.getAskForDismissPushData(this.askForExitArr, 0);
                    await this.sendRoomData(message);
                }
            }
        } else {

            //已经取消解散等发送一个空数组表示关闭
            let curTime = this.askDissmissTime ? this.askDissmissTime.diff(moment(), 's') : 0;
            let message = roomProto.getAskForDismissPushData(this.askForExitArr, curTime);
            await this.sendRoomData(message, [uid]);
        }
    }

    //是否可以删除
    isShouldDelete() {
        return (moment().diff(this.createTime, 'h') >= 24);
    }

    ownUser(uid) {
        let userArr = this.userArr;
        for (let key in userArr) {
            if (userArr.hasOwnProperty(key) && (userArr[key].userInfo.uid === uid)) {
                return true;
            }
        }
        return false;
    }

    /*-------------------------发送数据基础方法---------------------------------*/

    //获取玩家推送基础信息 
    async getUidAndSid(uidArr = null) {
        let uidAndSidArr = [];
        for (let key in this.userArr) {
            let user = this.userArr[key];
            let isSend = uidArr ? uidArr.includes(key) : true;
            if (user && !user.userInfo.isRobot && isSend && user.userStatus != roomProto.userStatusEnum.OFFLINE) {
                let sid = user.userInfo.frontendId;
                if (!sid || sid == '') {
                    let curUser = await userDao.getUserDataByUid(key);
                    sid = curUser && curUser.frontendId;
                    this.userArr[key].userInfo.frontendId = sid;
                }
                uidAndSidArr.push({ uid: key, sid: sid });
            }
        }
        return uidAndSidArr;
    }

    //发送消息
    async sendRoomData(msg, uidArr = null) {
        uidArr = uidArr ? uidArr.slice() : null;
        let uidAndSidArr = await this.getUidAndSid(uidArr);
        if (uidAndSidArr.length === 0) return;
        pushAPI.roomMessagePush(msg, uidAndSidArr);
    }

    async sendRoomDataExceptUid(msg, uidArr) {
        let uidList = [];
        for (let key in this.userArr) {
            let user = this.userArr[key];
            if (user && uidArr.indexOf(key) === -1) {
                uidList.push(key);
            }
        }
        await this.sendRoomData(msg, uidList);
    }

    //发送冒泡
    async sendPopDialogContent(code, content, uidArr = null) {

        let uidAndSidArr = await this.getUidAndSid(uidArr);
        if (uidAndSidArr.length === 0) return;
        pushAPI.popDialogContentPush({
            code: code,
            content: content
        }, uidAndSidArr);
    }

    async sendPopDialogContentExceptUid(code, content) {
        let uidList = [];
        for (let key in this.userArr) {
            let user = this.userArr[key];
            if (user && uidArr.indexOf(key) === -1) {
                uidList.push(key);
            }
        }
        await this.sendPopDialogContent(code, content, uidList);
    }

    async sendGameData(msg, uidArr = null) {
        uidArr = uidArr ? uidArr.slice() : null;
        let uidAndSidArr = await this.getUidAndSid(uidArr);
        if (uidAndSidArr.length === 0) return;
        pushAPI.gameMessagePush(msg, uidAndSidArr);
    }

    sendGameDataExceptUid(msg, uidArr) {
        let uidList = [];
        for (let key in this.userArr) {
            let user = this.userArr[key];
            if (user && uidArr.indexOf(key) === -1) {
                uidList.push(key);
            }
        }
        this.sendGameData(msg, uidList);
    }

    //发送游戏状态
    sendGameStatus(gameStatus, tick) {
        let msg = roomProto.gameStatusPush(gameStatus, tick);
        this.sendGameData(msg);
    }

    //发送自己进入房间消息
    async sendSelfEntryRoomData(userInfo) {

        if (!userInfo.isRobot) {
            let uidAndSidArr = await this.getUidAndSid([userInfo.uid]);
            let gameData = this.getEnterGameData(userInfo.uid);
            let userInfoArr = this.getUserInfoArr();
            pushAPI.selfEntryRoomPush(roomProto.selfEntryRoomPush(this.gameType, this.gameRule, this.roomID, gameData, userInfoArr, this.creatorInfo), uidAndSidArr);
        }
    }



    /*-------------------------基础工具类---------------------------------*/
    //清除调试牌型
    clearTestCards(testCards) {

        let userList = this.getPlayingUserList();
        for (let key in testCards) {
            if (!userList.includes(key)) {
                delete testCards[key];
            }
        }
        return testCards;
    }

    addKickScheduleEvent(uid, tick) {

        if (this.roomGameStarted) return;
        if (this.autoReadyTimer[uid]) {
            clearTimeout(this.autoReadyTimer[uid]);
            delete this.autoReadyTimer[uid];
        }
        this.showLog(`[${uid}] add user ready kick timer`);
        this.autoReadyTimer[uid] = setTimeout(async () => {
            if (this.autoReadyTimer[uid]) {
                delete this.autoReadyTimer[uid];
            }
            let user = this.userArr[uid];
            if (!this.roomGameStarted && user && user.playStatus === 0) {
                this.showLog(`[${uid}] user not ready kick out`);
                //玩家超时踢出去后  不能准备了
                this.isCanNotReady[uid] = true;
                await this.kickUser(uid).catch(e => { logger.error(e.stack) });
                //游戏是否可以开始
                this.checkIsCanStartGame(uid);
                // 判断房间是否解散
                await this.checkIsCanDismissRoom(uid);
            }
        }, tick * 1000);
    };

    //获取下家位置
    getNextUserID(userId) {
        if (!this.userArr[userId]) return null;
        let chairID = this.userArr[userId].chairID;
        for (let i = 1; i <= this.chairCount; ++i) {
            let nextChairID = (chairID + i) % this.chairCount;
            let user = this.getUserByChairID(nextChairID);
            if (user && nextChairID < this.chairCount && user.playStatus == roomProto.userPlayEnum.PLAYING) {
                return user.userInfo.uid;
            }
        }
        return null;
    }

    //获取默认列表
    getDefaultList(value) {

        let list = new Array(this.chairCount);
        if (value && Object.getPrototypeOf(value) === Array.prototype) {
            list.fill(value.slice());
        } else {
            list.fill(value);
        }
        return list;
    }

    //获取随机庄家
    getDefaultBanker() {

        let playingUser = this.getPlayingUserList();
        let rndIndex = utils.getRandomNum(0, playingUser.length - 1);
        return playingUser[rndIndex];
    }

    //克隆
    cloneObject(obj) {
        return JSON.parse(JSON.stringify(obj));
    }

    //是否未联盟创建
    isCreateByUnion(creatorInfo) {
        return creatorInfo.creatorType == enumeration.roomCreatorType.UNION_CREATE;
    }

    //是否金币房
    isCreateByGold(creatorInfo) {
        return creatorInfo.roomType == enumeration.roomType.GOLD;
    }



    initLogger(name) {
        this.gameLogger = require('pomelo-logger').getLogger(name);
    }

    //显示日志
    showLog(message, isError = false, logName = 'logic') {
        let title = `[${this.roomID}][${this.curBureau}]`;
        let curlogger = this.gameLogger ? this.gameLogger : require('pomelo-logger').getLogger(logName);
        if (isError) {
            curlogger.error(`${title} ${message} `);
        } else {
            curlogger.info(`${title} ${message} `);
        }
    }


    //延迟时间
    async delay(time) {
        return new Promise((resolve, reject) => {
            setTimeout(() => {
                resolve();
            }, time);
        });
    }

    //判断牌是否在当前的牌库中
    isInCardList(curData, allData) {

        curData = curData.slice();
        allData = allData.slice();
        let indexList = [];
        for (let i = 0; i < curData.length; i++) {
            let curIndex = allData.findIndex(item => item == curData[i]);
            if (curIndex >= 0) {
                indexList.push(curIndex);
                allData.splice(curIndex, 1);
            }
        }
        let isSuccess = false;
        if (indexList.length == curData.length) {
            isSuccess = true;
        }
        return isSuccess;
    }

    //根据uid或者可以看到的手牌
    getHandCardsByUid(uid, defalutCode = 0) {

        let curUser = this.getUserByUID(uid);
        if (curUser && curUser.userInfo.isTest) {
            //调试权限可以看所有牌
            return this.cloneObject(this.handCards);
        } else {
            let curCloneCards = {};
            for (let key in this.handCards) {
                let curCards = this.handCards[key].slice();
                if (key == uid) {
                    curCloneCards[key] = curCards;
                } else {
                    curCloneCards[key] = curCards.fill(defalutCode);
                }
            }
            return curCloneCards;
        }
    }

    getHandCardsByChairID(chairID, defalutCode = 0) {

        let curUser = this.getUserByChairID(chairID);
        if (curUser && curUser.userInfo.isTest) {
            //调试权限可以看所有牌
            return this.cloneObject(this.handCards);
        } else {
            let curCloneCards = {};
            for (let key in this.handCards) {
                if (this.handCards[key]) {
                    let curCards = this.handCards[key].slice();
                    if (key == chairID) {
                        curCloneCards[key] = curCards;
                    } else {
                        curCloneCards[key] = curCards.fill(defalutCode);
                    }
                } else {
                    curCloneCards[key] = null;
                }
            }
            return curCloneCards;
        }
    }

    /*-------------------------即将废弃---------------------------------*/
    updateLotteryInfo(resultLotteryInfo) {

    }



}

module.exports = roomBaseFrame;